package lol.clann.bukkit.pluginboot.model.gui;

import com.fasterxml.jackson.annotation.JsonIgnore;
import lol.clann.bukkit.pluginboot.model.IndexedWrapper;
import lol.clann.bukkit.pluginboot.util.InventoryUtils;
import lol.clann.clannboot.annotation.inject.Inject;
import lol.clann.clannboot.context.BeanContext;
import lol.clann.clannboot.util.collection.CollectionUtils;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.inventory.Inventory;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Stream;

/**
 * @param <S> cotent区
 */
public class View<S extends IconSupplier> {
    @Inject
    @JsonIgnore
    protected InventoryUtils inventoryUtils;
    @JsonIgnore
    private Function<ClickAction<InventoryClickEvent, S>, Boolean> clickAction;
    private String title;
    private List<Element<S>> elements;
    private InventoryWrapper wrapper;
    private Inventory inventory;
    private int row = 6;
    private int column = 9;
    private int capacity = 54;
    private String pluginId;
    private int size = 0;
    /**
     * 用于分页时,存储页码
     */
    private int page;

    public View() {
        BeanContext.inject(this);
    }

    public View(String pluginId, String title) {
        this();
        this.title = title;
        this.pluginId = pluginId;
        this.elements = new ArrayList<>(row * column);
        CollectionUtils.fill(this.elements, null, getCapacity());
        buildInventory();
    }

    /**
     * 正向查找第一个空格子
     *
     * @return
     */
    public int leftFirstEmptySlotIndex() {
        for (int i = 0; i < elements.size(); i++) {
            if (elements.get(i) == null) {
                return i;
            }
        }
        return -1;
    }

    /**
     * 逆向查找第一个空格子
     *
     * @return
     */
    public int rightFirstEmptySlotIndex() {
        for (int i = elements.size() - 1; i >= 0; i--) {
            if (elements.get(i) == null) {
                return i;
            }
        }
        return -1;
    }

    /**
     * 这是一个空格子
     * 这个格子之后全是空格子
     * 满足上述条件的第一个格子
     *
     * @param index 在指定索引之前查找
     * @return
     */
    public int appendEmptySlotIndex(int index) {
        for (int i = index; i >= 0; i--) {
            if (elements.get(i) != null) {
                return i == index ? -1 : i + 1;
            }
        }
        return -1;
    }

    public boolean isEmpty() {
        return size() == 0;
    }

    public void sync() {
        stream().forEach(iw -> setInventoryItemStack(iw.getIndex(), iw.getObject()));
    }

    int getPage() {
        return page;
    }

    View setPage(int page) {
        this.page = page;
        return this;
    }

    public int size() {
        return size;
    }

    public void destroy() {
        clear();
        inventory = null;
        wrapper.desroty();
        wrapper = null;
    }

    public void clear() {
        CollectionUtils.fill(elements, null, getCapacity());
        inventory.clear();
    }

    /**
     * 参数设置完后,调用此方法构建gui
     */
    public void buildInventory() {
        wrapper = new InventoryWrapper(Bukkit.createInventory(null, 54, title));
        wrapper.setClick(this::proccessClick)
                .setDrag(event -> event.setCancelled(true))
                .setPickupItem(event -> event.setCancelled(true))
                .build();
        inventory = wrapper.getInventory();
        clear();
    }

    /**
     * 遍历gui
     *
     * @return
     */
    public Stream<IndexedWrapper<Element<S>>> stream() {
        int[] index = {0};
        return elements.stream()
                .map(ele ->
                        new IndexedWrapper<>(index[0]++, ele)
                );
    }

    /**
     * @param index 格子的index
     * @return
     */
    public Element<S> getElement(int index) {
        return elements.get(index);
    }

    /**
     * 传入null为清空
     *
     * @param index 格子的index
     * @return 被替换的元素
     */
    public Element<S> setElement(int index, Element<S> element) {
        Element<S> replaced = elements.set(index, element);
        if (replaced != null) {
            size--;
        }
        if (element != null) {
            size++;
        }
        setInventoryItemStack(index, element);
        return replaced;
    }

    /**
     * 直接操作gui
     *
     * @param index
     * @param element
     */
    private void setInventoryItemStack(int index, Element<S> element) {
        inventory.setItem(index, element == null ? null : element.getIcon());
    }

    public int getCapacity() {
        return capacity;
    }

    public int getRow() {
        return row;
    }

    public int getColumn() {
        return column;
    }

    public String getTitle() {
        return title;
    }

    public void setClickAction(Function<ClickAction<InventoryClickEvent, S>, Boolean> clickAction) {
        this.clickAction = clickAction;
    }

    private <E extends InventoryClickEvent> void proccessClick(E event) {
        event.setCancelled(true);
        int slotIndex = event.getRawSlot();
        if (slotIndex < 0 || slotIndex >= getCapacity()) {
            return;
        }
        Element<S> ele = getElement(slotIndex);
        if (ele == null) {
            return;
        }
        Function<ClickAction<InventoryClickEvent, S>, Boolean> clickAction = ele.getClickAction();
        if (clickAction == null) {
            clickAction = this.clickAction;
        }
        if (clickAction == null) {
            return;
        }
        ClickAction<InventoryClickEvent, S> action = new ClickAction<>(this, event, ele, inventory, slotIndex);
        boolean changed = clickAction.apply(action);
        if (changed) {
            //Sponge上必须下一个tick刷新物品,bukkit无所谓
            this.setElement(action.getSlotIndex(), ele);
//            Bukkit.getScheduler().runTaskLater(plugin, () -> this.setElement(action.getSlotIndex(), ele), 1);
        }
    }

    //若当前tick打开,会导致鼠标上的物品丢出去,设置cancel后,等同于刷物品
    public void open(Player player) {
        player.openInventory(inventory);
//        Bukkit.getScheduler().runTaskLater(plugin, () -> player.openInventory(inventory), 1);
    }

}
